#pragma once

#include "Epoll.h"
#include "Error.h"
#include "ListenSocket.h"
#include "Log.h"
#include "Protocol.h"
#include <string>
#include <functional>
#include <unordered_map>

namespace server {
    // 声明
    struct Connection;
    class Server; 

    using func_t = std::function<void (Connection*)>;

    // 对sock进行封装
    struct Connection {
        static constexpr int DefaultSockfd = -1;

        int _sockfd;
        std::string _in_buffer; // 输入缓冲区
        std::string _out_buffer; // 输出缓冲区

        func_t _receive_method; // 处理输入事件的方法
        func_t _send_method; // 处理输出事件的方法
        func_t _except_method; // 处理异常事件的方法

        Server* _server; // 指向持有该Connection的服务器的指针，方便调用Server中的成员方法

        Connection(const int& sockfd, Server* server)
            : _sockfd(sockfd)
            , _server(server)
        {}

        ~Connection() {
            close();
        }

        // 注册Connection对各个事件的处理方法
        void registerMethod(const func_t& receive_method, const func_t& send_method, const func_t& except_method) {
            _receive_method = receive_method;
            _send_method = send_method;
            _except_method = except_method;
        }

        void close() {
            if (_sockfd != DefaultSockfd) {
                ::close(_sockfd);
                _sockfd = DefaultSockfd;
            }
        }

    }; //@end struct Connection


    class Server {
    public:
        static constexpr uint16_t DefaultPort = 18989;
        static constexpr int DefaultReceiveEventsLen = 32;
        static constexpr int BufferSize = 1024;

    private:
        uint16_t _port;
        ListenSocket _listen_socket;
        Epoll _epoll;
        std::unordered_map<int, Connection*> _connections;
        struct epoll_event* _receive_events;
        int _receive_events_len;
        func_t _service; // 服务器处理请求的方法

    public:
        Server(const func_t& service, const uint16_t& port = DefaultPort)
            : _port(port)
            , _receive_events(nullptr)
            , _receive_events_len(DefaultReceiveEventsLen)
            , _service(service)
        {}

        ~Server() {
            _port = DefaultPort;
            _listen_socket.close();
            _epoll.close();

            for (auto& pair : _connections) {
                delete pair.second;
            }

            if (_receive_events != nullptr) {
                delete[] _receive_events;
                _receive_events = nullptr;
            }
            _receive_events_len = -1;
            _service = nullptr;
        }

    public:
        // 初始化服务器
        void init() {
            //task 创建listen套接字并完成相关处理
            _listen_socket.create();
            _listen_socket.bind(_port);
            _listen_socket.listen();

            //task 创建epoll模型
            _epoll.create();

            //task 将listen套接字放进epoll和connections中
            add(_listen_socket.getFD(), EPOLLIN | EPOLLET,
            std::bind(&Server::acceptNewConnection, this),
            nullptr, nullptr);

            _receive_events = new struct epoll_event[_receive_events_len];
            bzero(_receive_events, sizeof(struct epoll_event) * _receive_events_len);
        }

        void run() {
            int timeout = 1000;
            for (;;) {
                react(timeout);
            }
        }

    private:
        /**
         * 用sockfd构建出一个Connection对象，将其放进Connections中组织起来，并让epoll进行托管
         * 
         * @param sockfd 需要构建成连接的sockfd
         * @param events sockfd需要让epoll进行关心的事件
         * @param receive_method 处理输入事件的方法
         * @param send_method 处理输出事件的方法
         * @param except_method 处理异常事件的方法
         * @return true表示添加成功，false表示添加失败
        */
        bool add(const int& sockfd, const uint32_t events, const func_t& receive_method, const func_t& send_method, const func_t& except_method) {
            //task 构建Connection对象
            Connection* connection = new Connection(sockfd, this);
            connection->registerMethod(receive_method, send_method, except_method);

            //task 将Connection对象组织起来
            _connections[sockfd] = connection;

            //task 让epoll对sockfd进行托管
            bool ret = _epoll.add(sockfd, events);
            if (ret == false) {
                logMessage(FATAL, "epoll add fail!");
                exit(EPOLL_ADD_ERR);
            }
        }

        /**
         * 处理输入事件
         * 
         * @param connection 需要处理输入事件的连接
        */
        void handleReceiveEvent(Connection* connection) {
            char buffer[BufferSize] = {};

            for (;;) {
                ssize_t s = recv(connection->_sockfd, buffer, BufferSize - 1, 0/* 虽然这里设置成了阻塞式读取，但是因为sockfd设置成的非阻塞，所以还是非阻塞式读取 */);
                if (s > 0) {
                    buffer[s] = '\0'; // 这里不是为了将buffer里的数据当字符串看，而是作为一个界限。方便将这些数据完整地放进Connection中的string做的缓冲区中。
                    connection->_in_buffer += buffer; // string的+=以'\0'为结尾
                    _service(connection); // 处理数据
                }
                else if (s == 0) {
                    if (connection->_except_method) connection->_except_method(connection); // 处理异常事件
                    return;
                }
                else {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) { // 没有数据可以读了
                        break;
                    }
                    else if (errno == EINTR) {
                        continue;
                    }
                    else {
                        if (connection->_except_method) connection->_except_method(connection); // 处理异常事件
                        return;
                    }
                }
            }
        }

        /**
         * 处理输出事件
         * 
         * @param connection 需要处理输出事件的连接
        */
        void handleSendEvent(Connection* connection) {
            for (;;) {
                ssize_t s = send(connection->_sockfd, connection->_out_buffer.c_str()/* 从发送缓冲区里把数据发送出去 */, connection->_out_buffer.size(), 0);
                if (s > 0) {
                    connection->_out_buffer.erase(0, s);
                    if (connection->_out_buffer.empty()) {
                        // _epoll.modify(connection->_sockfd, EPOLLIN | EPOLLOUT); // 如果没有数据可以发送了，就让epoll再帮忙关心一下输入事件
                        return;
                    }
                }
                else {
                    if (errno == EWOULDBLOCK || errno == EAGAIN) {
                        // 与 if (connection->_out_buffer.empty()) 重复了，可以删除一个
                        break;
                    }
                    else if (errno == EINTR) {
                        continue;
                    }
                    else {
                        if (connection->_except_method) connection->_except_method(connection);
                        return;
                    }
                }
            }
        }

        /**
         * 处理异常事件
         * 
         * @param connection 需要处理异常事件的连接
        */
        void handleExceptEvent(Connection* connection) {
            _epoll.remove(connection->_sockfd);
            _connections.erase(connection->_sockfd);
            connection->close();
            delete connection;
        }

        //从listen套接字中获取新连接
        void acceptNewConnection() {
            for (;;) {
                std::string client_ip;
                uint16_t client_port;
                int sockfd = _listen_socket.accept(&client_ip, &client_port);
                if (sockfd >= 0) {
                    add(sockfd, EPOLLIN | EPOLLET,
                    std::bind(&Server::handleReceiveEvent, this, std::placeholders::_1),
                    std::bind(&Server::handleSendEvent, this, std::placeholders::_1),
                    std::bind(&Server::handleExceptEvent, this, std::placeholders::_1));
                }
                else {
                    if (errno == EWOULDBLOCK || errno == EAGAIN) {
                        break;
                    }
                    else if (errno == EINTR) {
                        continue;
                    }
                    else {
                        logMessage(ERROR, "acceptNewConnection fail!");
                        return;
                    }
                }
            }
        }

        /**
         * 让epoll等待事件的发生，并对事件进行处理(事件驱动)
         * 
         * @param timeout epoll每次等待的时间长度。timeout == -1为阻塞式等待; timeout == 0为非阻塞式等待; timeout > 0为阻塞式等待timeout(ms)，没等到就直接返回
        */
        void react(const int& timeout) {
            int n = _epoll.wait(_receive_events, _receive_events_len, timeout);
            if (n == -1) {
                logMessage(ERROR, "wait events fail in react!");
                return;
            }

            for (int i = 0; i < n; ++i) {
                int sockfd = _receive_events[i].data.fd;
                uint32_t events = _receive_events[i].events;

                //task 将异常事件转化为读写事件，因为我们在处理读写事件时已经考虑了读写异常的问题。
                if (events & EPOLLERR) events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP) events |= (EPOLLIN | EPOLLOUT);

                //task 处理读写事件
                //* 这里不再需要对listen套接字的连接事件进行特判了！因为连接事件就是listen套接字的输入事件，这里统一处理了！
                if ((events & EPOLLIN)/* 读事件存在 */ && isConnectionExist(sockfd) /* sockfd所属连接存在 */&& _connections[sockfd]->_receive_method/* 对读事件的处理方法存在 */)
                    _connections[sockfd]->_receive_method(_connections[sockfd]);
                if ((events & EPOLLOUT)/* 写事件存在 */ && isConnectionExist(sockfd) /* sockfd所属连接存在 */&& _connections[sockfd]->_send_method/* 对写事件的处理方法存在 */)
                    _connections[sockfd]->_send_method(_connections[sockfd]);
            }
        }

        // 判断是否存在sockfd所属的连接
        bool isConnectionExist(const int& sockfd) {
            auto iter = _connections.find(sockfd);
            return iter != _connections.end();
        }

    }; //@end class Server
} //@end namespace server